#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/v4l2-subdev.h>
#include <android/log.h>
#include <pthread.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>

#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <fstream>
#include <iomanip>
#include <sstream>

#include "MLObjectarray.h"
//hwmpjepg
#include "MpiJpegDecoder.h"
#include "rkvpu_dec_api.h"


namespace android {
namespace hardware {
namespace camera {
namespace device {
namespace V3_4 {
namespace implementation {



#define TAG "MLCAM"

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,    TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,     TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,     TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,    TAG, __VA_ARGS__)
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,    TAG, __VA_ARGS__)

using namespace  std;

#define CLEAR(x) memset(&(x), 0, sizeof(x))
#define   BUFFER_COUNT         4




typedef unsigned char u8;
typedef unsigned int u32;

#define ml_fourcc(a,b,c,d) ((u32) (a) | ((u32) (b) << 8) | ((u32) (c) << 16) | ((u32) (d) << 24))
#define V4L2_PIX_FMT_H265 ml_fourcc('H', '2', '6', '5')



typedef struct _V4L2_Frame
{
    int             mValid;
    void*           data;
    /** like V4l2_buffer bytesused */
    size_t          data_bytes;
    uint32_t        length;
    uint32_t        width;
    uint32_t        height;
    uint8_t         frame_format;
    size_t          step;
    uint32_t        sequence;
    /** Estimate of system time  timestamp */
    struct timeval  timestamp;
    /** Is the data buffer owned by the library?
     * If 1, the data buffer can be arbitrarily reallocated by frame conversion
     * functions.
     * If 0, the data buffer will not be reallocated or freed by the library.
     * Set this field to zero if you are supplying the buffer.
    */
    uint8_t         library_owns_data;
}V4L2_frame_t;

#define DEFAULT_PREVIEW_WIDTH 2560
#define DEFAULT_PREVIEW_HEIGHT 1440
#define DEFAULT_BANDWIDTH 1.0f
#define MAX_FRAME 4
#define PREVIEW_PIXEL_BYTES 4	// RGBA/RGBX
#define PIXEL_BYTES_RGB     3   // RGB
#define FRAME_POOL_SZ MAX_FRAME + 2
typedef int (*convFunc_t)(V4L2_frame_t *in, V4L2_frame_t *out);

#define PIXEL_RGB565		2
#define PIXEL_UYVY			2
#define PIXEL_YUYV			2
#define PIXEL_RGB			3
#define PIXEL_BGR			3
#define PIXEL_RGBX			4

#define PIXEL2_RGB565		PIXEL_RGB565 * 2
#define PIXEL2_UYVY			PIXEL_UYVY * 2
#define PIXEL2_YUYV			PIXEL_YUYV * 2
#define PIXEL2_RGB			PIXEL_RGB * 2
#define PIXEL2_BGR			PIXEL_BGR * 2
#define PIXEL2_RGBX			PIXEL_RGBX * 2

#define PIXEL4_RGB565		PIXEL_RGB565 * 4
#define PIXEL4_UYVY			PIXEL_UYVY * 4
#define PIXEL4_YUYV			PIXEL_YUYV * 4
#define PIXEL4_RGB			PIXEL_RGB * 4
#define PIXEL4_BGR			PIXEL_BGR * 4
#define PIXEL4_RGBX			PIXEL_RGBX * 4

#define PIXEL8_RGB565		PIXEL_RGB565 * 8
#define PIXEL8_UYVY			PIXEL_UYVY * 8
#define PIXEL8_YUYV			PIXEL_YUYV * 8
#define PIXEL8_RGB			PIXEL_RGB * 8
#define PIXEL8_BGR			PIXEL_BGR * 8
#define PIXEL8_RGBX			PIXEL_RGBX * 8

#define PIXEL16_RGB565		PIXEL_RGB565 * 16
#define PIXEL16_UYVY		PIXEL_UYVY * 16
#define PIXEL16_YUYV		PIXEL_YUYV * 16
#define PIXEL16_RGB			PIXEL_RGB * 16
#define PIXEL16_BGR			PIXEL_BGR * 16
#define PIXEL16_RGBX		PIXEL_RGBX * 16

#define RGB2RGBX_2(prgb, prgbx, ax, bx) { \
		(prgbx)[bx+0] = (prgb)[ax+0]; \
		(prgbx)[bx+1] = (prgb)[ax+1]; \
		(prgbx)[bx+2] = (prgb)[ax+2]; \
		(prgbx)[bx+3] = 0xff; \
		(prgbx)[bx+4] = (prgb)[ax+3]; \
		(prgbx)[bx+5] = (prgb)[ax+4]; \
		(prgbx)[bx+6] = (prgb)[ax+5]; \
		(prgbx)[bx+7] = 0xff; \
	}
#define RGB2RGBX_16(prgb, prgbx, ax, bx) \
	RGB2RGBX_8(prgb, prgbx, ax, bx) \
	RGB2RGBX_8(prgb, prgbx, ax + PIXEL8_RGB, bx +PIXEL8_RGBX);
#define RGB2RGBX_8(prgb, prgbx, ax, bx) \
	RGB2RGBX_4(prgb, prgbx, ax, bx) \
	RGB2RGBX_4(prgb, prgbx, ax + PIXEL4_RGB, bx + PIXEL4_RGBX);
#define RGB2RGBX_4(prgb, prgbx, ax, bx) \
	RGB2RGBX_2(prgb, prgbx, ax, bx) \
	RGB2RGBX_2(prgb, prgbx, ax + PIXEL2_RGB, bx + PIXEL2_RGBX);



enum CAMSEQ{
    TOPCAM   = 1,
    SLIDECAM = 2,
    FACECAM  = 3,
    MICROCAM = 4,
};

#define TOPCAMNAME      "SETC03T"
#define SLIDECAMNAME    "SETC03S"
#define FACECAMNAME     "SETC03F"
#define MICROCAMNAME    "USB 2.0M CAM"

#define	MAXCNT  99999

/**
 *  JNI ENV
 */


class MlCamera {
private:
    string mCamDevName;
    int   mCAMFD;
    string mFilePrefix;
    string mProductName;

    string mStreamFileName;
    FILE   *mStreamFile;

    u8* mVideoBuffer[BUFFER_COUNT];
    u32 mBufferLength = 0;



    /**
     * flags camseq:  top->1 ,slide ->2 ,face ->3,micro ->4
     */

    bool mTop     = false;
    bool mSlide   = false;
    bool mFace    = false;

    const char MLCAMNODEPATH[64] = "/sys/class/video4linux/";

    /**
     * Frame info  default  400W
     *
     * #define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M', 'J', 'P', 'G')
     *
     * #define V4L2_PIX_FMT_H264 v4l2_fourcc('H', '2', '6', '4')
     *
     * #define V4L2_PIX_FMT_NV12 v4l2_fourcc('N', 'V', '1', '2')
     *
     * H265 系统暂时不支持 等RK   ;目前自定义
     *
     */

    int  mWidth             = 1920;
    int  mHeight            = 1080;
    int  mFramePixelFormat  =  V4L2_PIX_FMT_MJPEG;


    volatile bool mIsRunning;
    float mRequestBandwidth;
    int mFrameWidth, mFrameHeight;
    int mPixelFormat;
    size_t frameBytes;
    pthread_t preview_thread;
    pthread_t mFrame_thread;
    pthread_mutex_t preview_mutex;
    pthread_mutex_t callback_mutex;
    pthread_cond_t preview_sync;
    MLObjectarray<V4L2_frame_t *> previewFrames;
    int previewFormat;
    size_t previewBytes;
    V4L2_frame_t *latestFrameQueu;			// keep latest frame
    long mMaxWaitTime;
    long mMaxIOCTLTime;
    long mJNICALLTime;


    //hwmjpeg
    MpiJpegDecoder mDecoder;
    MpiJpegDecoder::OutputFrame_t mFrameOut;


    // improve performance by reducing memory allocation
    pthread_mutex_t pool_mutex;
    MLObjectarray<V4L2_frame_t *> mFramePool;
    //V4L2_frame_t *get_preview_frame(void);
    V4L2_frame_t *get_frame(size_t data_bytes);
    void recycle_frame(V4L2_frame_t *frame);
    void init_pool(size_t data_bytes);
    void clear_pool();


    //preview
    static void preview_frame_callback(void *vptr_args);
    void addPreviewFrame(V4L2_frame_t *frame);
    V4L2_frame_t *waitPreviewFrame();
    void clearPreviewFrame();

    int prepare_preview(V4L2_frame_t *ctrl);
    void do_preview(V4L2_frame_t *ctrl);



    /**
     * debug info
     */

    int mPreviewCnt;

public:
    MlCamera();

    int cameraInit(int camSeq);

    int openCAM();



    int set_format(int pixelformat, int width, int height);

    int init_buffer();

    int set_stream(int on);

    int startRecord();

    inline const bool isRunning() const;

    void do_preview();

    int get_frame(int frameNum,u8 *frame);

    V4L2_frame_t *get_preview_frame(void);

    static void *preview_thread_func(void *vptr_args);



    int stopRecord();


    int uninit_buffer();


    int closeCAM();

    ~MlCamera();

public:
    void V4L2_freeFrame(V4L2_frame_t *frame);
    V4L2_frame_t *V4L2_allocateFrame(size_t data_bytes);
    static int  V4L2_duplicateFrame(V4L2_frame_t *in, V4L2_frame_t *out);
    static int  V4L2_ensureFrameSize(V4L2_frame_t * frame, size_t need_bytes);
    static void V4L2_NV12TORGB(unsigned int width,unsigned  int height,unsigned char * yuyv, unsigned char *rgb);
    static int  V4L2_RGB2RGBX(V4L2_frame_t *in,V4L2_frame_t *out);
    static long getCurrentTime();

public:
    static MlCamera  *topcam;
    static MlCamera  *slidecam;
    static MlCamera  *facecam;

};

}  // namespace implementation
}  // namespace V3_4
}  // namespace device
}  // namespace camera
}  // namespace hardware
}  // namespace android
